package com.dc.datacollect.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;

/**
 * excel工具
 *        ExcelUtils.read() // 进入读模式
 *                 .loadExcel(path) // 加载Excel文件
 *                 .build()  // 进行构建
 *                 .list(0,User.class,users); // 提取到 “users”中（需要提取new 一个数组容器，要求类型与第二个参数相同）
 *
 *         ExcelUtils.write()  // 进入写模式
 *                 .addSheet(users,User.class,"新生2") // 添加 sheet
 *                 .bulid()  // 构建
 *                 .save(savePath) // 保存到本地
 *                 .download(response,downloadName) // 导出（返回后，浏览器进行下载）
 *                 .close(); // 关闭资源
 * @author manzhuangjie
 * @date 2022/11/29
 */
public class ExcelUtils {
    /**
     * 选择读模式
     * @return
     */
    public static ExcelReadOperator read() {
        return new ExcelReadOperator();
    }
    /**
     * 选择写模式：
     * @return
     */
    public static ExcelWriteOperator write() {
        return new ExcelWriteOperator();
    }


    /**
     * Excel写操作对象：封装了关于Excel写的操作方法
     * @author manzhuangjie
     * @date 2022/11/30
     */
    @Data
    public static class ExcelWriteOperator {
        private ExcelWriter excelWriter;
        private ByteOutputStream outputStream;
        {
            outputStream = new ByteOutputStream();
            excelWriter = EasyExcel.write(this.outputStream).build();
        }

        /**
         * 向初始化的Excel文件添加存储单元 Sheet
         * @param list      列表
         * @param tClass    对应的实体类型
         * @param sheetSign 表名字/索引
         * @return {@link ExcelWriteOperator}
         */
        private  <T>  ExcelWriteOperator abstractAddSheet(List<Object> list,Class<T> tClass, Object sheetSign) {
            ExcelWriterSheetBuilder sheetBuilder = null;
                    if(sheetSign instanceof Integer) {
                        sheetBuilder = EasyExcel.writerSheet((Integer) sheetSign);
                    }else if (sheetSign instanceof String) {
                        sheetBuilder = EasyExcel.writerSheet((String) sheetSign);
                    }
            WriteSheet sheet = sheetBuilder.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    // .registerWriteHandler(horizontalCellStyleStrategy)
                    // 设置类型
                    .head(tClass)
                    .build();
            // 设置类型第二种方式
            // sheet.setClazz(t);
            excelWriter.write(list,sheet);
            return this;
        }


        public <T>  ExcelWriteOperator addSheet(List<T> list, Object sheetIndexOrName) {
            return this.abstractAddSheet((List<Object>) list,list.get(0).getClass(),sheetIndexOrName);
        }
        public <T>  ExcelWriteOperator addSheet(List<T> list,Class<T> tClass, Object sheetIndexOrName) {
            return this.abstractAddSheet((List<Object>) list,tClass,sheetIndexOrName);
        }
        public ExcelWriteOperator bulid() {
            excelWriter.finish();
            return this;
        }
        public ExcelWriteOperator download (HttpServletResponse response,String downloadName) {
            ByteInputStream is = null;
            ServletOutputStream os = null;
            try {
                os = response.getOutputStream();
                is = this.outputStream.newInputStream();
                // 设置下载文件名
                response.setContentType("application/octet-stream");
                response.setCharacterEncoding("UTF-8");
                response.setHeader("Content-Disposition",
                        "attachment;filename="+ new String(downloadName.getBytes("gb2312"),"ISO8859-1" ));
                // 读取流
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                response.setCharacterEncoding("utf-8");
                // 将读取的内容进行写
                StreamUtils.isDumpOs(is,os);
            }catch (Exception e) {
                System.out.println("Excel的工具类内的download方法操作失败！");
            }finally {
                StreamUtils.closeStream(is,os);
            }
            return this;
        }
        public ExcelWriteOperator save(String path) {
            FileOutputStream os = null;
            ByteInputStream is = null;
            try {
                os = new FileOutputStream(path);
                is = outputStream.newInputStream();
                // 将读取的内容进行写
                StreamUtils.isDumpOs(is,os);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                System.out.println("[ERROR] Excel保存到本地失败！");
            }finally {
                StreamUtils.closeStream(is,os);
            }
            return this;
        }
        // 关闭资源
        public void close() {
            StreamUtils.closeStream(null,outputStream);
        }
    }
    @Data
    public static class ExcelReadOperator {
        private ExcelReader excelReader;
        private ExcelReaderBuilder excelReaderBuilder;
        private  ExcelUtilsAnalysisEventListener listener = new ExcelUtilsAnalysisEventListener();

        static class ExcelUtilsAnalysisEventListener<T> extends AnalysisEventListener<T> {
            private List<T> container;
            public void setContainer(List<T> tc) {
                this.container = tc;
            }
            public List<T> getContainer() {
                return container;
            }
            public ExcelUtilsAnalysisEventListener(List<T> container) {
                this.container = container;
            }
            public ExcelUtilsAnalysisEventListener() {
            }

            @Override
            public void invoke(T t, AnalysisContext analysisContext) {
                if (container == null) {
                    try {
                        throw new Exception("读取时未设置存放容器！");
                    } catch (Exception e) {
                        System.out.println("[ERROR] "+e.getMessage());
                    }
                }
                // 加入存储容器
                container.add(t);
            }


            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                System.out.println("OK,Excel read done !");
            }
        }
        public ExcelReadOperator loadExcel(String path) {
            this.excelReaderBuilder = EasyExcel.read(path, this.listener);
            return this;
        }
        public ExcelReadOperator load(File file) {
            this.excelReaderBuilder = EasyExcel.read(file, this.listener);
            return this;

        }
        public ExcelReadOperator load(MultipartFile multipartFile) {
            try {
                this.excelReaderBuilder = EasyExcel.read(multipartFile.getInputStream(), listener);
            } catch (IOException e) {
                System.out.println("ExcelUtils类 读取模式的load失败："+e.getMessage());
            }
            return this;
        }

        public ExcelReadOperator build() {
            this.excelReader = this.excelReaderBuilder.build();
            return this;
        }

        private <T> ExcelReadOperator listSpi(Object sheetSign,Class<T> t,List<T> dataContainter) {
            listener.setContainer(dataContainter);
            ReadSheet sheet = null;
            if (sheetSign instanceof Integer) {
                sheet = EasyExcel.readSheet((Integer) sheetSign).head(t).build();
            }else if (sheetSign instanceof String) {
                sheet = EasyExcel.readSheet((String) sheetSign).head(t).build();
            }
            this.excelReader.read(sheet);
            this.excelReader.finish();
            return this;
        }
        public  <T> ExcelReadOperator list(Integer sheetIndex,Class<T> t,List<T> dataContainter) {
            return this.listSpi(sheetIndex,t,dataContainter);
        }
        public  <T> ExcelReadOperator list(String sheetName,Class<T> t,List<T> dataContainter) {
            return this.listSpi(sheetName,t,dataContainter);
        }


    }
}

/**
 * 流操作工具
 */
class StreamUtils {
    public static void isDumpOs(InputStream is,OutputStream os) {

        try {
            // 创建缓冲区
            int len = 0;
            byte[] buffer = new byte[1024];
            // 获取OutputStream对象
            // 将FileOutputStream流写入到buffer缓冲区，使用OutputStream将缓冲区中的数据输出到客户端
            while ((len = is.read(buffer)) > 0) {
                os.write(buffer,0,len);
            }
            os.flush();
        }catch (Exception e) {
            System.out.println("StreamUtils类 -> isDumpOs方法 操作失败 ！");
        }
    }
    public static void closeStream(InputStream is,OutputStream os) {
        try {
            if (is != null ) {
                is.close();
            }
        } catch (IOException e) {}finally {
            try {
                if (is != null ) {
                    os.close();
                }
            } catch (IOException e) {}
        }
    }
}

